/*

  The contents of this file are subject to the Mozilla Public License Version
  1.1 (the "License"); you may not use this file except in compliance with
  the License. You may obtain a copy of the License at 
  
           http://www.mozilla.org/MPL/ 
  
  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License. 
  
  The Original Code is Indigo framework applicatif.
  
  The Initial Developer of the Original Code is
  Olivier Bugalotto (aka Iteratif) <olivier.bugalotto@iteratif.net>.
  Portions created by the Initial Developer are Copyright (C) 2004-2008
  the Initial Developer. All Rights Reserved.
  
  Contributor(s) :
  
*/
package indigo.factory
{
	import flash.utils.getQualifiedClassName;
	
	import indigo.AbstractClassError;
	import indigo.NoDefinitionError;
	import indigo.NotImplementedError;
	import indigo.collections.Hashtable;
	import indigo.config.IObjectDefinition;
	import indigo.register.IObjectDefinitionRegistry;
	
	/**
	 * La classe <code>AbstractObjectFactory</code> est la base du framework indigo
	 * c'est a partir de cette classe que vous pouvez construire, en vous appuyant sur
	 * la classe concrete <code>SimpleObjectFactory</code>, votre propre fabrique.
	 *
	 * @see SimpleObjectFactory
	 */
	public class AbstractObjectFactory implements IObjectFactory
	{
		// Gestion des singletons
		private var _singletons:Hashtable;
		protected var _register:IObjectDefinitionRegistry;
		
		/**
		 * Obtenir une reference du referentiel de definitions d'objets 
		 * @return 
		 * 
		 */		
		public function get register():IObjectDefinitionRegistry {
			return _register;
		}
		
		/**
		 * Creer un objet <code>AbstractObjectFactory</code> 
		 * 
		 * @param register	Reference a un objet <code>IObjectDefinitionRegistry</code>
		 * 
		 * @see indigo.register.IObjectDefinitionRegistry
		 */		
		public function AbstractObjectFactory(register:IObjectDefinitionRegistry) {
			if(getQualifiedClassName(this) == "indigo.factory.AbstractObjectFactory") {
				throw new AbstractClassError(AbstractObjectFactory);
			}
			
			_register = register;
			_singletons = new Hashtable();
		}
		
		/**
		 * Obtenir une instance de l'objet a partir de son nom d'identification dans le referentiel 
		 * @param name		nom de l'objet dans le referentiel
		 * @return 
		 * 
		 */		
		public function getObject(name:String):Object {
			if(!name) throw new ArgumentError();
			
			// Recuperer l'objet dans le cache des singletons
			var instance:Object = _singletons[name];
			if(!instance) { // Il n'existe pas
				// Recuperation de la definition de l'objet
				var definition:IObjectDefinition = register.getDefinition(name);
				
				if(!definition) // Elle n'existe pas
					throw new NoDefinitionError(name);
				
				if(definition.isSingleton) { // Si l'objet est un singleton
					// Creation et mise en cache de l'objet
					instance = createAndCacheSingletonInstance(name,definition);
				} else {
					// Creation de l'objet
					instance = createObject(name,definition);
				}
			}
			
			return instance;
		}
		
		/**
		 * Methode a redefinir pour specialiser la creation des objets 
		 * @param name			nom de l'objet dans le referentiel
		 * @param definition	definition de l'objet
		 * @return 				instance de l'objet
		 * 
		 */		
		protected function createObject(name:String, definition:IObjectDefinition):Object {
			throw new NotImplementedError(this.createObject);
		}
		
		/**
		 * Creation et mise en cache d'un singleton 
		 * @param name			nom de l'objet dans le referentiel
		 * @param definition	definition de l'objet
		 * @return 				instance de l'objet
		 * 
		 */		
		protected function createAndCacheSingletonInstance(name:String, definition:IObjectDefinition):Object {
			var instance:Object = createObject(name,definition);
			_singletons.addItem(name,instance);
			return instance;
		}
	}
}